package com.chuangke.sinotrukbudget.service.impl;

import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.druid.util.StringUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.chuangke.admin.data.builder.UserDataBuilder;
import com.chuangke.common.constant.Consts;
import com.chuangke.core.data.builder.DataBuildConfig;
import com.chuangke.core.data.builder.DataBuilder;
import com.chuangke.core.exception.ChuangkeException;
import com.chuangke.core.page.CkPageQuery;
import com.chuangke.framework.model.LevelModel;
import com.chuangke.framework.model.Record;
import com.chuangke.framework.util.TreeUtils;
import com.chuangke.sinotrukbudget.model.BudgetAccount;
import com.chuangke.sinotrukbudget.sdao.BudgetAccountDaoService;
import com.chuangke.sinotrukbudget.service.BudgetAccountService;

@Service
public class BudgetAccountServiceImpl implements BudgetAccountService {

    public static final String CACHE_NAME = "budget_account";

    @Autowired
    private BudgetAccountDaoService budgetAccountDaoService;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void save(BudgetAccount record) {
        ((BudgetAccountService) AopContext.currentProxy()).doSave(record);
        level();
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void save(List<BudgetAccount> records) {
        throw new ChuangkeException("暂不支持的方法");
    }

    /**
     * 对数据进行分级，更新数据的level,is_last_level,data_domain
     */
    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public void level() {
        List<BudgetAccount> list = findTree();
        List<BudgetAccount> newList = new ArrayList<>();
        TreeUtils.level("", 0, list, newList);
        doUpdateBatch(newList);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public void doSave(BudgetAccount record) {
        budgetAccountDaoService.saveOrUpdate(record);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    public void doUpdateBatch(List<BudgetAccount> list) {
        budgetAccountDaoService.updateBatchById(list);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public void delete(BudgetAccount record) {
        List<BudgetAccount> children = findAllChildren(Collections.singletonList(record.getId()));
        List<BudgetAccount> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        budgetAccountDaoService.removeBatchByIds(allRecords);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public void delete(List<BudgetAccount> records) {
        List<String> idList = records.stream().map(BudgetAccount::getId).collect(Collectors.toList());
        List<BudgetAccount> children = findAllChildren(idList);
        List<BudgetAccount> allRecords = TreeUtils.treeToList(children);
        if (CollectionUtils.isEmpty(allRecords)) {
            return;
        }
        budgetAccountDaoService.removeBatchByIds(records);
    }

    @Transactional(rollbackFor = Throwable.class)
    @CacheEvict(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public void deleteByIds(List<String> ids) {
        budgetAccountDaoService.removeByIds(ids);
    }

    @Cacheable(cacheNames = CACHE_NAME, key = "'" + CACHE_NAME + "'")
    @Override
    public Map<String, BudgetAccount> findMap() {
        List<BudgetAccount> list = budgetAccountDaoService.list();
        return list.stream().collect(Collectors.toMap(BudgetAccount::getId, a -> a));
    }

    @Override
    public List<BudgetAccount> findList() {
        Map<String, BudgetAccount> map = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        
        List<BudgetAccount> list = new ArrayList<>(map.values());
        DataBuilder.of(list)
        .appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
                new DataBuildConfig("updatedBy", "updatedByName")).build() ;
        return list ;
    }

    @Override
    public List<BudgetAccount> findTree() {
        List<BudgetAccount> depts = ((BudgetAccountService) AopContext.currentProxy()).findList();
        
        return TreeUtils.buildTree(depts);
    }

    @Override
    public List<BudgetAccount> findTree(String condition) {
        if (StrUtil.isBlank(condition)) {
            return findTree();
        }

        List<BudgetAccount> depts = ((BudgetAccountService) AopContext.currentProxy()).findList();
        List<String> ids = depts.stream().filter(dept -> dept.getId().contains(condition) || dept.getName().contains(condition)).map(BudgetAccount::getId).collect(Collectors.toList());
        return findKinshipTree(ids);
    }

    @Override
    public List<BudgetAccount> findListWithChildren() {
        List<BudgetAccount> list = findTree();
        return TreeUtils.treeToList(list);
    }

    @Override
    public BudgetAccount findById(String id) {
        if (id == null) {
            return null;
        }

        Map<String, BudgetAccount> map = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        return map.get(id);
    }

    @Override
    public List<BudgetAccount> findLastLevel() {
        List<BudgetAccount> treeList = findTree();

        List<BudgetAccount> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(treeList, lastLevelDeptList);

        return lastLevelDeptList;
    }

    @Override
    public List<BudgetAccount> findByLevel(int level) {
        List<BudgetAccount> list = ((BudgetAccountService) AopContext.currentProxy()).findList();
        return TreeUtils.findByLevel(list, level);
    }

    @Override
    public List<BudgetAccount> findLastLevel(String deptId) {
        List<BudgetAccount> list = findListWithChildren();
        Map<String, BudgetAccount> map = list.stream().collect(Collectors.toMap(BudgetAccount::getId, a -> a));

        List<BudgetAccount> deptTreeList = new ArrayList<>();
        deptTreeList.add(map.get(deptId));

        List<BudgetAccount> lastLevelDeptList = new ArrayList<>();
        TreeUtils.findLastLevel(deptTreeList, lastLevelDeptList);
        return lastLevelDeptList;
    }

    @Override
    public List<BudgetAccount> findNextLevel(String deptId) {
        List<BudgetAccount> list = findListWithChildren();
        Map<String, BudgetAccount> map = list.stream().collect(Collectors.toMap(BudgetAccount::getId, a -> a));
        return map.get(deptId) == null ? null : map.get(deptId).getChildren();
    }

    @Override
    public List<BudgetAccount> findKinshipTree(Collection<String> deptIdList) {
        Map<String, BudgetAccount> noChildMap = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        List<BudgetAccount> withChildrenList = findListWithChildren();
        
        DataBuilder.of(withChildrenList)
        .appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
                new DataBuildConfig("updatedBy", "updatedByName")).build() ;
        DataBuilder.of(noChildMap.values())
        .appendBuilder(new UserDataBuilder(), new DataBuildConfig("createdBy", "createdByName"),
                new DataBuildConfig("updatedBy", "updatedByName")).build() ;
        
        return TreeUtils.findKinshipTree(noChildMap, withChildrenList, deptIdList);
    }

    @Override
    public BudgetAccount findParent(String deptId) {
        Map<String, BudgetAccount> noChildMap = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        return TreeUtils.getParent(deptId, noChildMap);
    }

    @Override
    public List<BudgetAccount> findAllChildren(List<String> idList) {
        List<BudgetAccount> list = findListWithChildren();
        list = list.stream().filter(dept -> idList.contains(dept.getId())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        List<BudgetAccount> result = new ArrayList<>();
        TreeUtils.foreachChildren(result, list);

        return result;
    }

    @Override
    public IPage<BudgetAccount> page(CkPageQuery page) {
        throw new ChuangkeException("暂不支持的方法");
    }

    @Override
    public int getMaxLevel() {
        Map<String, BudgetAccount> map = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        return map.values().stream().mapToInt(LevelModel::getLevel).max().orElse(0);
    }

    public List<BudgetAccount> clone(List<BudgetAccount> accountList) {
        List<BudgetAccount> newAccountList = new ArrayList<>();
        for (BudgetAccount account : accountList) {
            BudgetAccount newAccount;
            try {
                newAccount = (BudgetAccount) BeanUtils.cloneBean(account);
                newAccountList.add(newAccount);
                if (!CollectionUtils.isEmpty(account.getChildren())) {
                    newAccount.setChildren(clone(account.getChildren()));
                }
            } catch (Exception e) {
                throw new ChuangkeException(e);
            }
        }
        return newAccountList;
    }

    @Override
    public Map<String, Record> getStandardData() {
        Map<String, BudgetAccount> map = ((BudgetAccountService) AopContext.currentProxy()).findMap();
        List<Record> list = new ArrayList<>(map.size());
        for (BudgetAccount ba : map.values()) {
            Record record = new Record();
            record.put(Consts.COL_NAME_CODE, ba.getId());
            record.put(Consts.COL_NAME_NAME, ba.getName());
            record.put(Consts.COL_NAME_LEVEL, ba.getLevel());
            record.put(Consts.COL_NAME_IS_LAST_LEVEL, ba.getIsLastLevel());
            record.put(Consts.COL_NAME_DATA_DOMAIN, ba.getDataDomain());
            record.put(Consts.COL_NAME_ID, ba.getId());
            record.put(Consts.COL_NAME_PARENT_ID, ba.getParentId());
            list.add(record);
        }
        return list.stream().collect(Collectors.toMap(record -> record.getStr(Consts.COL_NAME_CODE), a -> a));
    }

}
